package org.agony2d.gpu.core {
	import org.agony2d.core.UpdaterManager;
	import org.agony2d.core.agony_internal;
	import org.agony2d.debug.Logger;
	import org.agony2d.gpu.core.animation.ActionAA;
	import org.agony2d.gpu.core.animation.FrameClip;
	import org.agony2d.gpu.core.animation.SheetAA;
	import org.agony2d.gpu.supportClasses.buffers.BuffersManager;
	import org.agony2d.utils.MathUtil;
	
	use namespace agony_internal;
	
	/** [ AnimatorAA ]
	 *  [◆]
	 *  	1.  isPlaying
	 *  	2.  frame
	 *  	3.  speedRatio
	 *  [◆◆]
	 *  	1.  setActionByClipName
	 *  	2.  setActionByClipIndex
	 *  	3.  getAction
	 *  	4.  play
	 *  [★]
	 *  	a.  regionTex: sheetName + "_" + 002 / 013 / 101
	 */
public class AnimatorAA extends PixelsAA {
	
    public function AnimatorAA( sheet:SheetAA ) {
		if (!sheet) {
			Logger.reportError(this, "constructor", "Sheet ( " + sheet.m_sheetName + " ) can't be null...")
		}
		m_sheet = sheet
		m_actionList = []
		m_speedRatio = m_currFrame = 1
    }
	
	final public function get isPlaying() : Boolean {
		return Boolean(m_actionList)
	}
	
	final public function get frame() : uint {
		return m_currFrame
	}
	
	public function set frame( v:uint ) : void {
		m_currFrame = MathUtil.bound(v, 1, m_sheet.totalFrames) 
		m_execActionIndexList = null
	}
	
	final public function get speedRatio() : Number {
		return m_speedRatio
	}
    
	public function set speedRatio( v:Number ) : void {
		m_speedRatio = (v < 0 || isNaN(v)) ? 0 : v
	}
	
	public function setActionByClipName( actionIndex:int, clipName:String, totalRepeat:int = 1 ) : ActionAA {
		return this.doAddAction(actionIndex, m_sheet.m_clipMap[clipName], totalRepeat)
	}
	
	public function setActionByClipIndex( actionIndex:int, clipIndex:int, totalRepeat:int = 1 ) : ActionAA {
		return this.doAddAction(actionIndex, m_sheet.m_clipList[clipIndex], totalRepeat)
	}
	
	public function getAction( index:int ) : ActionAA {
		return m_actionList[index]
	}
	
	public function play( actionIndexes:Array, totalRepeat:int = 0 ) : void {
		if (actionIndexes && actionIndexes.length > 0) {
			m_execActionIndexList = actionIndexes
			m_totalRepeat = totalRepeat
			m_execActionIndex = 0
			this.doResetAction()
			m_actionTotalCount = m_currAction.m_totalRepeat
		}
		else {
			m_execActionIndexList = null
		}
	}
	
	
	
	agony_internal static var g_globalSpeedRatio:Number = 1
	agony_internal static var g_texSplit:String = "_"
		
	agony_internal var m_sheet:SheetAA
	agony_internal var m_speedRatio:Number
	agony_internal var m_currFrame:int
	agony_internal var m_prevFrame:int
	
	agony_internal var m_currTime:Number
	agony_internal var m_currAction:ActionAA
	agony_internal var m_clip:FrameClip
	agony_internal var m_frames:Vector.<int>
	agony_internal var m_actionList:Array
	agony_internal var m_execActionIndex:int
	
	agony_internal var m_numframesInAction:int
	agony_internal var m_frameIndex:int
	agony_internal var m_execActionIndexList:Array
	agony_internal var m_totalRepeat:int // all action repeat count
	agony_internal var m_actionTotalCount:int // action self repeat count
	
	
	[Inline]
	final override agony_internal function doUpdateUpward() : void {
		var node:NodeAA
		
		if (m_altered) {
			if (m_currFrame != m_prevFrame) {
				m_renderTex = m_fullTex.getRegion(m_sheet.m_sheetName + g_texSplit + String(m_currFrame))
			}
			this.updateByAlone()
		}
		else if (m_currFrame != m_prevFrame) {
			m_renderTex = m_fullTex.getRegion(m_sheet.m_sheetName + g_texSplit + String(m_currFrame))
			if (m_renderTex.m_frame) {
				this.updateByAlone()
			}
		}
		cachedMatrix.copyFrom(m_matrix_A)
		node = m_parent
		while (node && !(node is WorldAA)) {
			if (node.m_altered) {
				node.updateByAlone()
			}
			cachedMatrix.concat(node.m_matrix_A)
			node = node.m_parent
		}
	}
	
	[Inline]
	final override agony_internal function attach( parent:NodeAA ) : void {
		if (!parent.m_fullTex) {
			Logger.reportError(this, "attach", "Parent hasn't been added to any GroupAA...!!")
		}
		if (m_parent) {
			if (m_fullTex != parent.m_fullTex) {
				Logger.reportError(this, "attach", "Full Texture alters...!!")
			}
			m_parent.removeNode(this)
			m_altered = true
		}
		else {
			m_fullTex = parent.m_fullTex
		}
		m_parent = parent
		m_world = parent.m_world
	}
	
	[Inline]
	final agony_internal function doAddAction( actionIndex:int, clip:FrameClip, totalRepeat:int ) : ActionAA {
		var action:ActionAA
		
		if (!clip) {
			Logger.reportError(this, "doAddAction", "Null frame clip...")
		}
		if (m_actionList[actionIndex]) {
			Logger.reportError(this, "doAddAction", "Existed action...!!")
		}
		action = m_actionList[actionIndex] = new ActionAA(clip, totalRepeat)
		return action
	}
	
	[Inline]
	final agony_internal function doResetAction() : void {
		m_currTime = m_frameIndex = 0
		m_currAction = m_actionList[m_execActionIndexList[m_execActionIndex]]
		m_clip = m_currAction.m_clip
		m_currFrame = m_clip.frames[0]
		m_numframesInAction = m_clip.totalFrames
	}
	
	[Inline]
	final agony_internal function doNextAction() : void {
		var callback:Function
		
		callback = m_currAction.m_callback
		this.doResetAction()
		if (callback != null) {
			callback()
		}
	}
	
	[Inline]
	final override agony_internal function doPushToConvergence( vertexChanged:Boolean ) : void {
		if (m_execActionIndexList) {
			if (m_currTime >= m_clip.roundTime) {
				this.doAdvance()
			}
			m_currTime += (UpdaterManager.g_elapsed > 300 ? 300 : UpdaterManager.g_elapsed) * m_currAction.m_speedRatio * m_speedRatio * g_globalSpeedRatio
		}
		if (vertexChanged) {
			if (m_currFrame != m_prevFrame) {
				m_renderTex = m_fullTex.getRegion(m_sheet.m_sheetName + g_texSplit + String(m_currFrame))
				m_combine.setUV(m_renderTex)
				m_prevFrame = m_currFrame
			}
			if (m_altered || m_renderTex.m_frame) {
				this.doUpdateDownward()
				m_altered = false
			}
			else {
				m_matrix_B.copyFrom(m_matrix_A)
			}
			m_matrix_B.concat(m_parent.m_matrix_B)
			m_combine.setVertexCoords(m_renderTex.m_realityWidth, m_renderTex.m_realityHeight, m_matrix_B)
		}
		else if (m_altered) {
			if (m_currFrame != m_prevFrame) {
				m_renderTex = m_fullTex.getRegion(m_sheet.m_sheetName + g_texSplit + String(m_currFrame))
				m_combine.setUV(m_renderTex)
				m_prevFrame = m_currFrame
			}
			this.doUpdateDownward()
			m_altered = false
			m_matrix_B.concat(m_parent.m_matrix_B)
			m_combine.setVertexCoords(m_renderTex.m_realityWidth, m_renderTex.m_realityHeight, m_matrix_B)
		}
		else if (m_currFrame != m_prevFrame) {
			m_renderTex = m_fullTex.getRegion(m_sheet.m_sheetName + g_texSplit + String(m_currFrame))
			m_combine.setUV(m_renderTex)
			m_prevFrame = m_currFrame
			if (m_renderTex.m_frame) {
				this.doUpdateDownward()
				m_matrix_B.concat(m_parent.m_matrix_B)
				m_combine.setVertexCoords(m_renderTex.m_realityWidth, m_renderTex.m_realityHeight, m_matrix_B)
			}
			else {
				m_combine.setVertexCoordsFixed(m_renderTex.m_realityWidth, m_renderTex.m_realityHeight, m_matrix_B)
			}
		}
		BuffersManager.g_bytes.writeBytes(m_combine.m_bytes)
	}
	
	[Inline]
	final agony_internal function doAdvance() : void {
		m_currTime -= m_clip.roundTime
		
		// frame list tail
		if (++m_frameIndex >= m_numframesInAction) {
			// into next action，otherwise repeat in action
			if (m_actionTotalCount > 0 && --m_actionTotalCount <= 0) {
				// action list tail
				if (++m_execActionIndex >= m_execActionIndexList.length) {
					// repeat in action
					if (m_totalRepeat <= 0 || --m_totalRepeat > 0) {
						m_execActionIndex = 0
					}
					// no next action
					else {
						if (m_currAction.m_callback != null) {
							m_currAction.m_callback()
							m_execActionIndexList = null
						}
						return
					}
				}
				this.doNextAction()
				m_actionTotalCount = m_currAction.m_totalRepeat
			}
			else {
				this.doNextAction()
			}
		}
		m_currFrame = m_clip.frames[m_frameIndex]
		//trace("currFrame: " + m_currFrame)
		if (m_execActionIndexList) {
			if (m_currTime >= m_clip.roundTime) {
				this.doAdvance()
			}
		}
	}
}
}